package org.example.dao;

import org.jgrapht.graph.DefaultDirectedWeightedGraph;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.junit.Before;
import org.junit.Test;
import org.junit.After;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

import static org.junit.Assert.assertTrue;

public class WordGraphTestBaiHe {

  private WordGraph wordGraph;
  private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
  private final PrintStream originalOut = System.out;

  @Before
  public void setUp() {
    wordGraph = new WordGraph();
    System.setOut(new PrintStream(outContent));
  }

  @After
  public void tearDown() {
    System.setOut(originalOut);
  }

  @Test
  public void testRandomWalkGraphEmpty() throws IOException {
    wordGraph.randomWalk();
    assertTrue(outContent.toString().contains("Graph is empty!"));
  }

  @Test
  public void testRandomWalkNoOutgoingEdges() throws IOException {
    wordGraph.graph.addVertex("A");
    wordGraph.randomWalk();
    String output = outContent.toString();
    assertTrue(output.contains("Starting random walk from node: A"));
    assertTrue(output.contains("No outgoing edges from node A. Random walk ended."));
  }

  @Test
  public void testRandomWalkVisitedEdge() throws IOException {
    String v1 = "A";
    String v2 = "B";
    wordGraph.graph.addVertex(v1);
    wordGraph.graph.addVertex(v2);
    DefaultWeightedEdge e1 = wordGraph.graph.addEdge(v1, v2);
    DefaultWeightedEdge e2 = wordGraph.graph.addEdge(v2, v1);
    wordGraph.graph.setEdgeWeight(e1, 1.0);
    wordGraph.graph.setEdgeWeight(e2, 1.0);

    // Simulate user input: "yes", "yes", then "yes"
    List<String> inputs = Arrays.asList("yes", "yes", "yes");
    System.setIn(new CustomInputStream(inputs));
    wordGraph.randomWalk();
    String output = outContent.toString();
    System.out.println("Actual output: " + output); // Print actual output for debugging

    assertTrue(output.contains("Starting random walk from node:"));
    assertTrue(output.contains("Next node:"));
    assertTrue(output.contains("Continue random walk? (yes/no):"));
    assertTrue(output.contains("Edge " + e2 + " has already been visited. Exiting random walk."));
  }


  @Test
  public void testRandomWalkUserStops() throws IOException {
    String v1 = "A";
    String v2 = "B";
    wordGraph.graph.addVertex(v1);
    wordGraph.graph.addVertex(v2);
    DefaultWeightedEdge e1 = wordGraph.graph.addEdge(v1, v2);
    wordGraph.graph.setEdgeWeight(e1, 1.0);
    DefaultWeightedEdge e2 = wordGraph.graph.addEdge(v2, v1);
    wordGraph.graph.setEdgeWeight(e2, 1.0);

    // Simulate user input: "no"
    List<String> inputs = Arrays.asList("no","no");
    System.setIn(new CustomInputStream(inputs));
    wordGraph.randomWalk();
    String output = outContent.toString();

    assertTrue(output.contains("Starting random walk from node:"));
    assertTrue(output.contains("Next node:"));
    assertTrue(output.contains("Continue random walk? (yes/no):"));
    assertTrue(output.contains("Random walk stopped by user."));
  }

  @Test
  public void testRandomWalkNormal() throws IOException {
    String v1 = "A";
    String v2 = "B";
    String v3 = "C";
    wordGraph.graph.addVertex(v1);
    wordGraph.graph.addVertex(v2);
    wordGraph.graph.addVertex(v3);
    DefaultWeightedEdge e1 = wordGraph.graph.addEdge(v1, v2);
    DefaultWeightedEdge e2 = wordGraph.graph.addEdge(v2, v3);
    DefaultWeightedEdge e3 = wordGraph.graph.addEdge(v3, v1);
    wordGraph.graph.setEdgeWeight(e1, 1.0);
    wordGraph.graph.setEdgeWeight(e2, 1.0);
    wordGraph.graph.setEdgeWeight(e3, 1.0);

    // Simulate user input: multiple "yes" and then "no"
    List<String> inputs = Arrays.asList("yes", "yes", "yes", "yes");
    System.setIn(new CustomInputStream(inputs));
    wordGraph.randomWalk();
    String output = outContent.toString();
    System.out.println("Actual output: " + output); // Print actual output for debugging

    assertTrue(output.contains("Starting random walk from node:"));
    assertTrue(output.contains("Next node:"));
    assertTrue(output.contains("Continue random walk? (yes/no):"));
    assertTrue(output.contains("has already been visited. Exiting random walk."));
  }

  // Custom InputStream to simulate user input
  static class CustomInputStream extends ByteArrayInputStream {
    private final List<String> inputs;
    private int index;

    public CustomInputStream(List<String> inputs) {
      super(new byte[0]);
      this.inputs = inputs;
      this.index = 0;
    }

    @Override
    public synchronized int read(byte[] b, int off, int len) {
      if (index < inputs.size()) {
        byte[] inputBytes = (inputs.get(index) + "\n").getBytes();
        System.arraycopy(inputBytes, 0, b, off, inputBytes.length);
        index++;
        return inputBytes.length;
      }
      return -1;
    }
  }
}
